﻿using FluentValidation;
using FluentValidation.Resources;
using FluentValidation.Validators;
using Nop.Admin.Models.Orders;
using Nop.Core.Domain.Directory;
using Nop.Core.Domain.Logistics;
using Nop.Services.Common;
using Nop.Services.Directory;
using Nop.Services.Localization;
using System;

namespace Nop.Admin.Validators.Orders
{
    public abstract class MeasureValidator : PropertyValidator
    {
        protected LogisticsInformationSettings _logisticsInformationSettings;
        protected IMeasureService _measureService;
        protected MeasureDimension _dimension;
        protected MeasureWeight _weight;

        protected MeasureValidator(LogisticsInformationSettings logisticsInformationSettings, IMeasureService measureService)
            : base("The {PropertyName} is invalid.")
        {
            _logisticsInformationSettings = logisticsInformationSettings;
            _measureService = measureService;
        }

        protected override bool IsValid(PropertyValidatorContext context)
        {
            var v = context.PropertyValue as decimal?;

            if (!v.HasValue)
            {
                ErrorMessageSource = new StaticStringSource("The {PropertyName} is required.");
                return false;
            }

            var package = context.Instance as ShipmentPackageModel;
            if (package == null)
            {
                return false;
            }

            _dimension = _measureService.GetMeasureDimensionById(package.DimensionMeasureId);
            if (_dimension == null)
            {
                ErrorMessageSource = new StaticStringSource("The dimension unit is invalid.");
                return false;
            }

            _weight = _measureService.GetMeasureWeightById(package.WeightMeasureId);
            if (_weight == null)
            {
                ErrorMessageSource = new StaticStringSource("The weight unit is invalid.");
                return false;
            }

            if (v.Value <= 0)
            {
                ErrorMessageSource = new StaticStringSource(String.Format("The {{PropertyName}} must be greater than 0 {0}.", MeasureUnit));
                return false;
            }

            if (v.Value > Limitation)
            {
                ErrorMessageSource = new StaticStringSource(String.Format("The {{PropertyName}} may not exceed {0:D} {1}.", (int)Limitation, MeasureUnit));
                return false;
            }

            return true;
        }

        protected abstract string MeasureUnit { get; }

        protected abstract decimal Limitation { get; }
    }

    public class DimensionValidator : MeasureValidator
	{
        public DimensionValidator(LogisticsInformationSettings logisticsInformationSettings, IMeasureService measureService)
            : base(logisticsInformationSettings, measureService)
	    {
	    }

        protected override string MeasureUnit
        {
            get { return _dimension.Name; }
        }

        protected override decimal Limitation
        {
            get { return MathEx.Round(
                _measureService.ConvertFromPrimaryMeasureDimension(
                    _logisticsInformationSettings.SingleMaxDimensionSize, _dimension)
                    , 0); }
        }
    }

    public class WeightValidator : MeasureValidator
    {
        public WeightValidator(LogisticsInformationSettings logisticsInformationSettings, IMeasureService measureService)
            : base(logisticsInformationSettings, measureService)
        {
        }

        protected override string MeasureUnit
        {
            get { return _weight.Name; }
        }

        protected override decimal Limitation
        {
            get
            {
                return MathEx.Round(
                    _measureService.ConvertFromPrimaryMeasureWeight(
                    _logisticsInformationSettings.MaxWeight, _weight)
                    , 0);
            }
        }
    }

    public class ShipmentPackageValidator : AbstractValidator<ShipmentPackageModel>
    {
        public ShipmentPackageValidator(
            LogisticsInformationSettings logisticsInformationSettings,
            ILocalizationService localizationService,
            IMeasureService measureService)
        {
            CascadeMode = CascadeMode.StopOnFirstFailure;

            RuleFor(x => x.Length)
                .SetValidator(new DimensionValidator(logisticsInformationSettings, measureService))
                .Unless(x => x.SpotshubPackageSize > 0)
                //.Unless(x => x.IsLetterSize)
                .WithName("length");

            RuleFor(x => x.Width)
                .SetValidator(new DimensionValidator(logisticsInformationSettings, measureService))
                .Unless(x => x.SpotshubPackageSize > 0)
                //.Unless(x => x.IsLetterSize)
                .WithName("width");

            RuleFor(x => x.Height)
                .SetValidator(new DimensionValidator(logisticsInformationSettings, measureService))
                .Unless(x => x.SpotshubPackageSize > 0)
                //.Unless(x => x.IsLetterSize)
                .WithName("height");

            RuleFor(x => x.Weight)
                .SetValidator(new WeightValidator(logisticsInformationSettings, measureService))
                .Unless(x => x.SpotshubPackageSize > 0)
                //.Unless(x => x.IsLetterSize)
                .WithName("weight");

            //RuleFor(x => x.InsuranceValue.Value)
            //    .LessThanOrEqualTo(logisticsInformationSettings.MaxInsuranceValue)
            //    .When(x => x.InsuranceValue.HasValue)
            //    .WithMessage(string.Format("The declared value may not exceed ${0}.", logisticsInformationSettings.MaxInsuranceValue))
            //    .WithName("the insurance value");

            RuleFor(x => x.Comment)
                .Length(0, logisticsInformationSettings.MaxCommentLength)
                .When(x => x.Comment != null)
                .WithMessage("The length of the comment may not exceed {0} characters.", logisticsInformationSettings.MaxCommentLength);
        }
    }
}